home *** CD-ROM | disk | FTP | other *** search
- //////////
- //
- // File: MakeEffectMovie.c
- //
- // Contains: QuickTime video effect support for QuickTime movies.
- // This file is used for BOTH MacOS and Windows.
- //
- // Written by: Tim Monroe
- // Based (heavily!) on the previous MakeEffectMovie code written by Sam Bushell,
- // which was based on ConvertToMovie.c from ConvertToMovie Jr. sample code.
- //
- // Copyright: ⌐ 1997-1998 by Apple Computer, Inc., all rights reserved.
- //
- // Change History (most recent first):
- //
- // <5> 03/12/98 rtm added global flag gDoneWithDialog to fix Windows dialog box problems
- // <4> 02/28/98 rtm revised event/message handling following QTShowEffect.c
- // <3> 01/08/98 rtm sync'ed with latest code from Sam: reworked QTEffects_GetFirstVideoTrackInMovie
- // to use GetMovieIndTrackType and fixed time scale setting code; added global flag
- // gCopyMovieMedia and menu commands to set that flag
- // <2> 11/21/97 rtm factored out QTEffects_GetFirstVideoTrackInMovie;
- // reworked event-loop processing: changed QTEffects_MakeEffectMovieForSources into
- // QTEffects_DisplayDialogForSources and QTEffects_RespondToDialogSelection.
- // <1> 11/06/97 rtm first file; integrated existing code with shell framework;
- // added endian macros where appropriate
- //
- // This application takes the video tracks from zero, one, or two movies
- // and creates a new movie with an effects track for them.
- //
- // Try it out: drag two short movies of the same size onto the application icon.
- // The QuickTime effects dialog will appear and you can select an effect to transition
- // from the first movie to the second. Or, drag a single movie onto the application icon
- // and you'll get an effects dialog for one-source effects.
- //
- // You can also launch the application and choose the Make Effect Movie menu command in
- // the Test menu. You'll get a file-opening dialog box for each movie you want to apply
- // the effects to. Just hit Cancel in the first dialog box to get zero-source effects (like fire).
- //
- //////////
-
- // TO DO:
- // + copy sound tracks from original movie(s); add tweening of sound tracks as effect progresses
-
- // header files
- #include "MakeEffectMovie.h"
-
- // global variables
- QTParameterDialog gEffectsDialog = 0L; // identifier for the standard parameter dialog box
- QTAtomContainer gEffectSample = NULL; // effects sample
- QTAtomContainer gEffectList = NULL;
- PicHandle gPosterA = NULL;
- PicHandle gPosterB = NULL;
- Movie gSrcMovies[kMaxNumSources] = {NULL, NULL};
- Track gSrcTracks[kMaxNumSources] = {NULL, NULL};
- UInt16 gSpecCount = 0;
- Boolean gCopyMovieMedia = false; // should we copy the movie media into the new effects movie?
- Boolean gDoneWithDialog = false; // are we done using the effects parameters dialog box?
-
-
- //////////
- //
- // QTEffects_GetFirstVideoTrackInMovie
- // Return, through the theTrack parameter, the first video track in the specified movie.
- //
- // Actually, we look for the first track that has the kCharacteristicCanSendVideo characteristic,
- // so we can apply effects to MPEG or QD3D tracks as well.
- //
- // If no such track is found, return invalidTrack as the function result.
- //
- //////////
-
- OSErr QTEffects_GetFirstVideoTrackInMovie (Movie theMovie, Track *theTrack)
- {
- *theTrack = GetMovieIndTrackType(theMovie, 1, kCharacteristicCanSendVideo, movieTrackCharacteristic | movieTrackEnabledOnly);
-
- if (*theTrack == NULL)
- return(invalidTrack);
-
- return(noErr);
- }
-
-
- //////////
- //
- // QTEffects_DisplayDialogForSources
- // Display the standard effects parameters dialog box for the movies passed in.
- //
- //////////
-
- OSErr QTEffects_DisplayDialogForSources (FSSpec *theSpecList, UInt16 theSpecCount)
- {
- OSErr myErr = noErr;
- UInt16 myMovieIter;
-
- // make sure that there aren't too many sources
- if (theSpecCount > kMaxNumSources) {
- myErr = paramErr;
- goto bail;
- }
-
- // assign source count to a global, so QTEffects_RespondToDialogSelection has access to it
- gSpecCount = theSpecCount;
-
- // open the source movies, get movies from them, and get the first video track from each movie
- for (myMovieIter = 0; myMovieIter < theSpecCount; myMovieIter++) {
- short myRefNum;
-
- // open a movie file using the FSSpec and create a movie from that file
- myErr = OpenMovieFile(&theSpecList[myMovieIter], &myRefNum, 0);
- BailError(myErr);
-
- myErr = NewMovieFromFile(&gSrcMovies[myMovieIter], myRefNum, NULL, NULL, newMovieActive, NULL);
- BailError(myErr);
-
- // we're done with the movie file, so close it
- CloseMovieFile(myRefNum);
-
- // find the first video track in the source movie
- myErr = QTEffects_GetFirstVideoTrackInMovie(gSrcMovies[myMovieIter], &gSrcTracks[myMovieIter]);
- BailError(myErr);
- }
-
- // ask the user to select an effect
-
- myErr = QTNewAtomContainer(&gEffectSample);
- BailError(myErr);
-
- myErr = QTGetEffectsList(&gEffectList, theSpecCount, theSpecCount, 0);
- BailError(myErr);
-
- myErr = QTCreateStandardParameterDialog(gEffectList, gEffectSample, 0, &gEffectsDialog);
- BailError(myErr);
-
- // insert poster frames into dialog
- if (gSrcTracks[0] != NULL) {
- gPosterA = GetTrackPict(gSrcTracks[0], GetMoviePosterTime(gSrcMovies[0]));
- if (gPosterA != NULL) {
- QTParamPreviewRecord myPreviewRecord;
-
- myPreviewRecord.sourcePicture = gPosterA;
- myPreviewRecord.sourceID = 1;
- myErr = QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &myPreviewRecord);
- }
- }
-
- if (gSrcTracks[1] != NULL) {
- gPosterB = GetTrackPict(gSrcTracks[1], GetMoviePosterTime(gSrcMovies[1]));
- if (gPosterB != NULL) {
- QTParamPreviewRecord myPreviewRecord;
-
- myPreviewRecord.sourcePicture = gPosterB;
- myPreviewRecord.sourceID = 2;
- myErr = QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &myPreviewRecord);
- }
- }
-
- // now, the frontmost window is the standard effects parameter dialog box;
- // on the Mac, we call QTEffects_HandleEffectsDialogEvents in our main event loop
- // to find and process events targeted at the effects parameter dialog box; on Windows,
- // we need to use a different strategy: we install a modeless dialog callback procedure
- // that is called internally by QTML
-
- #if TARGET_OS_WIN32
- gDoneWithDialog = false;
-
- // force the dialog box to be drawn
- {
- EventRecord myEvent = {0};
-
- QTEffects_EffectsDialogCallback(&myEvent, FrontWindow(), 0);
- }
-
- SetModelessDialogCallbackProc(FrontWindow(), (ModelessCallbackUPP)QTEffects_EffectsDialogCallback);
- QTMLSetWindowWndProc(FrontWindow(), QTEffects_CustomDialogWndProc);
- #endif
-
- bail:
- return(myErr);
- }
-
-
- //////////
- //
- // QTEffects_RespondToDialogSelection
- // If theErr is codecParameterDialogConfirm, make an effects movie.
- // If theErr is userCanceledErr, do any necessary clean up.
- //
- //////////
-
- void QTEffects_RespondToDialogSelection (OSErr theErr)
- {
- short myMovieRefNum = 0;
- short myResID = movieInDataForkResID;
- Boolean myDialogWasCancelled = false;
- OSType myEffectCode;
- Fixed videoTrackFXWidth, videoTrackFXHeight;
- TimeScale myMovieTimeScale = 600;
- TimeValue myEffectDuration = 0;
- TimeValue mySampleTime = 0;
- StandardFileReply myReply;
- Movie myDestMovie = NULL;
- Track videoTracks[kMaxNumSources] = {NULL, NULL};
- Track videoTrackFX = NULL;
- Media videoMediaFX = NULL;
- UInt16 myMovieIter;
- long myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
- OSErr myErr = noErr;
-
- // standard parameter box has been dismissed, so remember that fact
- gEffectsDialog = 0L;
-
- myDialogWasCancelled = (theErr == userCanceledErr);
-
- // we're finished with the effect list and movie posters
- QTDisposeAtomContainer(gEffectList);
-
- if (gPosterA != NULL)
- KillPicture(gPosterA);
-
- if (gPosterB != NULL)
- KillPicture(gPosterB);
-
- if (myDialogWasCancelled)
- goto bail;
-
- // add atoms naming the sources to gEffectSample
- {
- long myLong;
-
- if (gSpecCount >= 1) {
- myLong = EndianU32_NtoB(kSourceOneName);
- QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myLong), &myLong, NULL);
- }
-
- if (gSpecCount >= 2) {
- myLong = EndianU32_NtoB(kSourceTwoName);
- QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myLong), &myLong, NULL);
- }
- }
-
- // extract the 'what' atom to find out what kind of effect it is
- {
- QTAtom myEffectAtom;
- QTAtomID myEffectAtomID;
- long myEffectCodeSize;
- Ptr myEffectCodePtr;
-
- myEffectAtom = QTFindChildByIndex(gEffectSample, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, &myEffectAtomID);
-
- myErr = QTLockContainer(gEffectSample);
- BailError(myErr);
-
- myErr = QTGetAtomDataPtr(gEffectSample, myEffectAtom, &myEffectCodeSize, &myEffectCodePtr);
- BailError(myErr);
-
- if (myEffectCodeSize != sizeof(OSType)) {
- myErr = paramErr;
- goto bail;
- }
-
- myEffectCode = *(OSType *)myEffectCodePtr; // "tsk"
- myEffectCode = EndianU32_BtoN(myEffectCode); // because the data is read from an atom container
-
- myErr = QTUnlockContainer(gEffectSample);
- BailError(myErr);
- }
-
- // ask the user for the name of the new movie file
- StandardPutFile("\pSave effect movie file as:", "\pUntitled.mov", &myReply);
- if (!myReply.sfGood)
- goto bail; // deal with user cancelling
-
- // create a movie file for the destination movie
- myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), 0, myFlags, &myMovieRefNum, &myDestMovie);
- BailError(myErr);
-
- // copy the user data and settings from the source to the destination movie;
- // these settings include information like user data
- if (gSpecCount > 0)
- CopyMovieSettings(gSrcMovies[0], myDestMovie);
-
- // convert all the movies to have a common time scale;
- // we pick the largest time scale out of all the source movies, with a minimum of 600
- myMovieTimeScale = 600;
- for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++) {
- if (myMovieTimeScale < GetMovieTimeScale(gSrcMovies[myMovieIter]))
- myMovieTimeScale = GetMovieTimeScale(gSrcMovies[myMovieIter]);
- }
-
- for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++) {
- if (myMovieTimeScale != GetMovieTimeScale(gSrcMovies[myMovieIter]))
- SetMovieTimeScale(gSrcMovies[myMovieIter], myMovieTimeScale);
- }
-
- // the effect duration is the minimum of the length of the tracks
- if (gSpecCount == 0)
- myEffectDuration = myMovieTimeScale * 10;
- else
- myEffectDuration = GetTrackDuration(gSrcTracks[0]);
-
- for (myMovieIter = 1; myMovieIter < gSpecCount; myMovieIter++) {
- if (myEffectDuration > GetTrackDuration(gSrcTracks[myMovieIter]))
- myEffectDuration = GetTrackDuration(gSrcTracks[myMovieIter]);
- }
-
- // default size when there are no video tracks
- videoTrackFXWidth = 160L << 16;
- videoTrackFXHeight = 120L << 16;
-
- for (myMovieIter = 0; myMovieIter < kMaxNumSources; myMovieIter++) {
- videoTracks[myMovieIter] = NULL;
- }
-
- // make the video tracks
- for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++) {
- Fixed mySrcTrackWidth, mySrcTrackHeight;
- OSType mySrcMediaType = 0;
- Media videoMedia;
-
- // videoTracks[n] is a clone of gSrcTracks[n]
- GetTrackDimensions(gSrcTracks[myMovieIter], &mySrcTrackWidth, &mySrcTrackHeight);
-
- if ((myMovieIter == 0) || (videoTrackFXWidth < mySrcTrackWidth))
- videoTrackFXWidth = mySrcTrackWidth;
-
- if ((myMovieIter == 0) || (videoTrackFXHeight < mySrcTrackHeight))
- videoTrackFXHeight = mySrcTrackHeight;
-
- GetMediaHandlerDescription(GetTrackMedia(gSrcTracks[myMovieIter]), &mySrcMediaType, NULL, NULL);
-
- videoTracks[myMovieIter] = NewMovieTrack(myDestMovie, mySrcTrackWidth, mySrcTrackHeight, 0);
- BailNil(videoTracks[myMovieIter]);
- videoMedia = NewTrackMedia(videoTracks[myMovieIter], mySrcMediaType, myMovieTimeScale, NULL, 0);
- BailNil(videoMedia);
-
- if (gCopyMovieMedia) {
- myErr = BeginMediaEdits(videoMedia);
- BailError(myErr);
- }
-
- myErr = CopyTrackSettings(gSrcTracks[myMovieIter], videoTracks[myMovieIter]);
- BailError(myErr);
- myErr = InsertTrackSegment(gSrcTracks[myMovieIter], videoTracks[myMovieIter], 0, myEffectDuration, 0);
- BailError(myErr);
-
- if (gCopyMovieMedia) {
- myErr = EndMediaEdits(videoMedia);
- BailError(myErr);
- }
- }
-
- {
- // videoTrackFX is the special track that implements the effect
- videoTrackFX = NewMovieTrack(myDestMovie, videoTrackFXWidth, videoTrackFXHeight, 0);
- BailNil(videoTrackFX);
- videoMediaFX = NewTrackMedia(videoTrackFX, VideoMediaType, myMovieTimeScale, NULL, 0);
- BailNil(videoMediaFX);
- }
-
- {
- ImageDescriptionHandle myDesc = NULL;
-
- // create a new sample description
- myDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
- BailNil(myDesc);
-
- // fill in the fields of the sample description
- (**myDesc).idSize = sizeof(ImageDescription);
- (**myDesc).cType = myEffectCode;
- (**myDesc).vendor = kAppleManufacturer;
- (**myDesc).temporalQuality = codecNormalQuality;
- (**myDesc).spatialQuality = codecNormalQuality;
- (**myDesc).width = videoTrackFXWidth >> 16;
- (**myDesc).height = videoTrackFXHeight >> 16;
- (**myDesc).hRes = 72L << 16;
- (**myDesc).vRes = 72L << 16;
- (**myDesc).dataSize = 0L;
- (**myDesc).frameCount = 1;
- (**myDesc).depth = 0;
- (**myDesc).clutID = -1;
-
- // add effects sample to movie
- myErr = BeginMediaEdits(videoMediaFX);
- BailError(myErr);
-
- myErr = AddMediaSample(videoMediaFX, gEffectSample, 0, GetHandleSize(gEffectSample), myEffectDuration, (SampleDescriptionHandle)myDesc, 1, 0, &mySampleTime);
- BailError(myErr);
-
- myErr = EndMediaEdits(videoMediaFX);
- BailError(myErr);
-
- QTDisposeAtomContainer(gEffectSample);
- DisposeHandle((Handle)myDesc);
- }
-
- myErr = InsertMediaIntoTrack(videoTrackFX, 0, mySampleTime, myEffectDuration, fixed1);
- BailError(myErr);
-
- // create and add the input map
- {
- long myRefIndex1, myRefIndex2;
- QTAtomContainer myInputMap;
- QTAtom myInputAtom;
- OSType myInputType;
- long myLong;
-
- QTNewAtomContainer(&myInputMap);
-
- // first input
- if (videoTracks[0]) {
- AddTrackReference(videoTrackFX, videoTracks[0], kTrackModifierReference, &myRefIndex1);
- QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex1, 0, 0, NULL, &myInputAtom);
-
- myInputType = EndianU32_NtoB(kTrackModifierTypeImage);
- QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL);
-
- myLong = EndianU32_NtoB(kSourceOneName);
- QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL);
- }
-
- // second input
- if (videoTracks[1]) {
- AddTrackReference(videoTrackFX, videoTracks[1], kTrackModifierReference, &myRefIndex2);
- QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex2, 0, 0, NULL, &myInputAtom);
-
- myInputType = EndianU32_NtoB(kTrackModifierTypeImage);
- QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL);
-
- myLong = EndianU32_NtoB(kSourceTwoName);
- QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL);
- }
-
- // set that map
- if (gSpecCount > 0)
- SetMediaInputMap(GetTrackMedia(videoTrackFX), myInputMap);
-
- QTDisposeAtomContainer(myInputMap);
- }
-
- myErr = AddMovieResource(myDestMovie, myMovieRefNum, &myResID, "\pMovie 1");
- BailError(myErr);
-
- CloseMovieFile(myMovieRefNum);
-
- for (myMovieIter = 0; myMovieIter < gSpecCount; myMovieIter++)
- DisposeMovie(gSrcMovies[myMovieIter]);
-
- DisposeMovie(myDestMovie);
-
- bail:
- return;
- }
-
-
- #if TARGET_OS_WIN32
- //////////
- //
- // QTEffects_EffectsDialogCallback
- // This function is called by QTML when it processes events for the standard or custom effects parameter dialog box.
- //
- //////////
-
- static void QTEffects_EffectsDialogCallback (EventRecord *theEvent, DialogRef theDialog, DialogItemIndex theItemHit)
- {
- QTParamDialogEventRecord myRecord;
-
- myRecord.theEvent = theEvent;
- myRecord.whichDialog = theDialog;
- myRecord.itemHit = theItemHit;
-
- if (gEffectsDialog != 0L) {
- QTStandardParameterDialogDoAction(gEffectsDialog, pdActionModelessCallback, &myRecord);
-
- // see if the event is meant for the effects parameter dialog box
- QTEffects_HandleEffectsDialogEvents(theEvent, theItemHit);
- }
- }
-
-
- //////////
- //
- // QTEffects_CustomDialogWndProc
- // Handle messages for the custom effects parameters dialog box.
- //
- //////////
-
- LRESULT CALLBACK QTEffects_CustomDialogWndProc (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
- {
- EventRecord myEvent = {0};
-
- if (!gDoneWithDialog && (theMessage == 0x7FFF))
- QTEffects_EffectsDialogCallback(&myEvent, GetNativeWindowPort(theWnd), 0);
-
- return(DefWindowProc(theWnd, theMessage, wParam, lParam));
- }
- #endif
-
-
- //////////
- //
- // QTEffects_HandleEffectsDialogEvents
- // Process events that might be targeted at the standard effects parameter dialog box.
- // Return true if the event was completely handled.
- //
- //////////
-
- Boolean QTEffects_HandleEffectsDialogEvents (EventRecord *theEvent, DialogItemIndex theItemHit)
- {
- Boolean isHandled = false;
- OSErr myErr = noErr;
-
- // pass the event to the standard effects parameter dialog box handler
- myErr = QTIsStandardParameterDialogEvent(theEvent, gEffectsDialog);
-
- // the result from QTIsStandardParameterDialogEvent tells us how to respond next
- switch (myErr) {
-
- case codecParameterDialogConfirm:
- case userCanceledErr:
- // the user clicked the OK or Cancel button; dismiss the dialog box and respond accordingly
- gDoneWithDialog = true;
- QTStandardParameterDialogDoAction(gEffectsDialog, pdActionConfirmDialog, NULL);
- QTDismissStandardParameterDialog(gEffectsDialog);
- gEffectsDialog = 0L;
- QTEffects_RespondToDialogSelection(myErr);
- isHandled = true;
- break;
-
- case noErr:
- // the event was completely handled by QTIsStandardParameterDialogEvent
- isHandled = true;
- break;
-
- case featureUnsupported:
- // the event was not handled by QTIsStandardParameterDialogEvent;
- // let the event be processed normally
- isHandled = false;
- break;
-
- default:
- // the event was not handled by QTIsStandardParameterDialogEvent;
- // do not let the event be processed normally
- isHandled = true;
- break;
- }
-
- return(isHandled);
- }
-
-
- //////////
- //
- // QTEffects_PromptUserForFilesAndMakeEffect
- // Let the user select some movies, then apply the effect to them.
- //
- // If the user cancels the first file-open dialog box, there are zero sources.
- // If the user cancels the second file-open dialog box, there is one source.
- //
- //////////
-
- void QTEffects_PromptUserForFilesAndMakeEffect (void)
- {
- FSSpec mySpecs[kMaxNumSources];
- int mySpecCount;
-
- // ask for up to kMaxNumSources movie files;
- // accept early cancels; they just mean there are fewer input movies
- mySpecCount = 0;
- while (mySpecCount < kMaxNumSources) {
- SFTypeList myTypeList;
- StandardFileReply myReply;
-
- myTypeList[0] = MovieFileType;
-
- StandardGetFilePreview(NULL, 1, myTypeList, &myReply);
- if (!myReply.sfGood)
- break; // the user doesn't want any more source movies
-
- // save the FSSpec from the reply information
- mySpecs[mySpecCount] = myReply.sfFile;
-
- mySpecCount++;
- }
-
- QTEffects_DisplayDialogForSources(mySpecs, mySpecCount);
- }
-